Una exploración en profundidad del Intercambio de Recursos de Origen Cruzado (CORS) y las solicitudes 'preflight'. Aprenda a manejar problemas de CORS y a proteger sus aplicaciones web para una audiencia global.
Desmitificando CORS: Una inmersión profunda en el manejo de solicitudes 'preflight' en JavaScript
En el mundo en constante expansión del desarrollo web, la seguridad es primordial. El Intercambio de Recursos de Origen Cruzado (CORS) es un mecanismo de seguridad crucial implementado por los navegadores web para restringir que las páginas web realicen solicitudes a un dominio diferente al que sirvió la página. Esta es una característica de seguridad fundamental diseñada para evitar que sitios web maliciosos accedan a datos sensibles. Esta guía completa profundizará en las complejidades de CORS, centrándose específicamente en el manejo de solicitudes 'preflight'. Exploraremos el 'porqué', el 'qué' y el 'cómo' de CORS, proporcionando ejemplos prácticos y soluciones a problemas comunes que enfrentan los desarrolladores en todo el mundo.
Entendiendo la Política del Mismo Origen
En el corazón de CORS se encuentra la Política del Mismo Origen (SOP). Esta política es un mecanismo de seguridad a nivel del navegador que restringe que los scripts que se ejecutan en un origen accedan a recursos de un origen diferente. Un origen se define por el protocolo (p. ej., HTTP o HTTPS), el dominio (p. ej., example.com) y el puerto (p. ej., 80 o 443). Dos URL tienen el mismo origen si estos tres componentes coinciden exactamente.
Por ejemplo:
https://www.example.com/app1/index.htmlyhttps://www.example.com/app2/index.htmltienen el mismo origen (mismo protocolo, dominio y puerto).https://www.example.com/index.htmlyhttp://www.example.com/index.htmltienen orígenes diferentes (protocolos diferentes).https://www.example.com/index.htmlyhttps://api.example.com/index.htmltienen orígenes diferentes (los subdominios diferentes se consideran dominios diferentes).https://www.example.com:8080/index.htmlyhttps://www.example.com/index.htmltienen orígenes diferentes (puertos diferentes).
La SOP está diseñada para evitar que scripts maliciosos en un sitio web accedan a datos sensibles, como cookies o información de autenticación de usuario, en otro sitio web. Si bien es esencial para la seguridad, la SOP también puede ser restrictiva, especialmente cuando se necesitan solicitudes legítimas de origen cruzado.
¿Qué es el Intercambio de Recursos de Origen Cruzado (CORS)?
CORS es un mecanismo que permite a los servidores especificar qué orígenes (dominios, esquemas o puertos) tienen permitido acceder a sus recursos. Esencialmente, relaja la SOP, permitiendo un acceso controlado de origen cruzado. CORS se implementa utilizando cabeceras HTTP que se intercambian entre el cliente (generalmente un navegador web) y el servidor.
Cuando un navegador realiza una solicitud de origen cruzado (es decir, una solicitud a un origen diferente al de la página actual), primero comprueba si el servidor permite la solicitud. Esto se hace examinando la cabecera Access-Control-Allow-Origin en la respuesta del servidor. Si el origen de la solicitud está en esta cabecera (o si la cabecera está configurada como *, permitiendo todos los orígenes), el navegador permite que la solicitud continúe. De lo contrario, el navegador bloquea la solicitud, impidiendo que el código JavaScript acceda a los datos de la respuesta.
El Rol de las Solicitudes 'Preflight'
Para ciertos tipos de solicitudes de origen cruzado, el navegador inicia una solicitud 'preflight' (de comprobación previa). Esta es una solicitud OPTIONS enviada al servidor antes de la solicitud real. El propósito de la solicitud 'preflight' es determinar si el servidor está dispuesto a aceptar la solicitud real. El servidor responde a la solicitud 'preflight' con información sobre los métodos, cabeceras y otras restricciones permitidas.
Las solicitudes 'preflight' se activan cuando la solicitud de origen cruzado cumple cualquiera de las siguientes condiciones:
- El método de la solicitud no es
GET,HEADoPOST. - La solicitud incluye cabeceras personalizadas (es decir, cabeceras distintas a las añadidas automáticamente por el navegador).
- La cabecera
Content-Typeestá configurada con un valor distinto aapplication/x-www-form-urlencoded,multipart/form-dataotext/plain. - La solicitud utiliza objetos
ReadableStreamen el cuerpo.
Por ejemplo, una solicitud PUT con un Content-Type de application/json activará una solicitud 'preflight' porque utiliza un método diferente a los permitidos y un tipo de contenido potencialmente no permitido.
¿Por qué las Solicitudes 'Preflight'?
Las solicitudes 'preflight' son esenciales para la seguridad porque brindan al servidor la oportunidad de rechazar solicitudes de origen cruzado potencialmente dañinas antes de que se ejecuten. Sin las solicitudes 'preflight', un sitio web malicioso podría enviar solicitudes arbitrarias a un servidor sin el consentimiento explícito del servidor. Una solicitud 'preflight' permite al servidor validar que la solicitud es aceptable y previene operaciones potencialmente dañinas.
Manejo de Solicitudes 'Preflight' en el Lado del Servidor
Manejar adecuadamente las solicitudes 'preflight' es crucial para garantizar que su aplicación web funcione de manera correcta y segura. El servidor debe responder a la solicitud OPTIONS con las cabeceras CORS apropiadas para indicar si la solicitud real está permitida.
Aquí hay un desglose de las cabeceras CORS clave que se utilizan en las respuestas 'preflight':
Access-Control-Allow-Origin: Esta cabecera especifica el/los origen(es) que tienen permitido acceder al recurso. Se puede establecer en un origen específico (p. ej.,https://www.example.com) o en*para permitir todos los orígenes. Sin embargo, usar*generalmente no se recomienda por razones de seguridad, especialmente si el servidor maneja datos sensibles.Access-Control-Allow-Methods: Esta cabecera especifica los métodos HTTP que están permitidos para la solicitud de origen cruzado (p. ej.,GET,POST,PUT,DELETE).Access-Control-Allow-Headers: Esta cabecera especifica la lista de cabeceras HTTP no estándar que están permitidas en la solicitud real. Esto es necesario si el cliente envía cabeceras personalizadas, comoX-Custom-HeaderoAuthorization.Access-Control-Allow-Credentials: Esta cabecera indica si la solicitud real puede incluir credenciales, como cookies o cabeceras de autorización. Debe establecerse entruesi el código del lado del cliente envía credenciales y el servidor debe aceptarlas. Nota: cuando esta cabecera se establece en `true`, `Access-Control-Allow-Origin` *no puede* establecerse en `*`. Debe especificar el origen.Access-Control-Max-Age: Esta cabecera especifica la cantidad máxima de tiempo (en segundos) que el navegador puede almacenar en caché la respuesta 'preflight'. Esto puede ayudar a mejorar el rendimiento al reducir el número de solicitudes 'preflight' que se envían.
Ejemplo: Manejo de Solicitudes 'Preflight' en Node.js con Express
Aquí hay un ejemplo de cómo manejar solicitudes 'preflight' en una aplicación Node.js usando el framework Express:
const express = require('express');
const cors = require('cors');
const app = express();
// Habilitar CORS para todos los orígenes (¡solo para fines de desarrollo!)
// En producción, especifique los orígenes permitidos para una mejor seguridad.
app.use(cors()); //o app.use(cors({origin: 'https://www.example.com'}));
// Ruta para manejar solicitudes OPTIONS (preflight)
app.options('/data', cors()); // Habilitar CORS para una sola ruta. O especificar el origen: cors({origin: 'https://www.example.com'})
// Ruta para manejar solicitudes GET
app.get('/data', (req, res) => {
res.json({ message: '¡Estos son datos de origen cruzado!' });
});
// Ruta para manejar una solicitud preflight y una solicitud post
app.options('/resource', cors()); // habilitar solicitud pre-flight para la solicitud DELETE
app.delete('/resource', cors(), (req, res, next) => {
res.send('recurso eliminado')
})
const port = 3000;
app.listen(port, () => {
console.log(`Servidor escuchando en el puerto ${port}`);
});
En este ejemplo, usamos el middleware cors para manejar las solicitudes CORS. Para un control más granular, CORS se puede habilitar por ruta. Nota: en producción, se recomienda encarecidamente especificar los orígenes permitidos usando la opción origin en lugar de permitir todos los orígenes. Permitir todos los orígenes usando * puede exponer su aplicación a vulnerabilidades de seguridad.
Ejemplo: Manejo de Solicitudes 'Preflight' en Python con Flask
Aquí hay un ejemplo de cómo manejar solicitudes 'preflight' en una aplicación Python usando el framework Flask y la extensión flask_cors:
from flask import Flask, jsonify
from flask_cors import CORS, cross_origin
app = Flask(__name__)
CORS(app) # Habilitar CORS para todas las rutas
@app.route('/data')
@cross_origin()
def get_data():
data = {"message": "¡Estos son datos de origen cruzado!"}
return jsonify(data)
if __name__ == '__main__':
app.run(debug=True)
Este es el uso más simple. Como antes, los orígenes pueden ser restringidos. Consulte la documentación de flask-cors para más detalles.
Ejemplo: Manejo de Solicitudes 'Preflight' en Java con Spring Boot
Aquí hay un ejemplo de cómo manejar solicitudes 'preflight' en una aplicación Java usando Spring Boot:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class CorsApplication {
public static void main(String[] args) {
SpringApplication.run(CorsApplication.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/data").allowedOrigins("http://localhost:8080");
}
};
}
}
Y el controlador correspondiente:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DataController {
@GetMapping("/data")
public String getData() {
return "¡Estos son datos de origen cruzado!";
}
}
Problemas Comunes de CORS y Soluciones
A pesar de su importancia, CORS a menudo puede ser una fuente de frustración para los desarrolladores. Aquí hay algunos problemas comunes de CORS y sus soluciones:
-
Error: "No 'Access-Control-Allow-Origin' header is present on the requested resource."
Este error indica que el servidor no está devolviendo la cabecera
Access-Control-Allow-Originen su respuesta. Para solucionarlo, asegúrese de que el servidor esté configurado para incluir la cabecera y que esté establecida en el origen correcto o en*(si es apropiado).Solución: Configure el servidor para incluir la cabecera `Access-Control-Allow-Origin` en su respuesta, estableciéndola en el origen del sitio web solicitante o en `*` para permitir todos los orígenes (úselo con precaución).
-
Error: "Response to preflight request doesn't pass access control check: Request header field X-Custom-Header is not allowed by Access-Control-Allow-Headers in preflight response."
Este error indica que el servidor no permite la cabecera personalizada (
X-Custom-Headeren este ejemplo) en la solicitud de origen cruzado. Para solucionarlo, asegúrese de que el servidor incluya la cabecera en la cabeceraAccess-Control-Allow-Headersen la respuesta 'preflight'.Solución: Agregue la cabecera personalizada (p. ej., `X-Custom-Header`) a la cabecera `Access-Control-Allow-Headers` en la respuesta 'preflight' del servidor.
-
Error: "Credentials flag is 'true', but the 'Access-Control-Allow-Origin' header is '*'."
Cuando la cabecera
Access-Control-Allow-Credentialsse establece entrue, la cabeceraAccess-Control-Allow-Origindebe establecerse en un origen específico, no en*. Esto se debe a que permitir credenciales de todos los orígenes sería un riesgo de seguridad.Solución: Cuando use credenciales, establezca `Access-Control-Allow-Origin` en un origen específico en lugar de `*`.
-
La solicitud 'preflight' no se está enviando.
Verifique dos veces que su código Javascript incluya la propiedad `credentials: 'include'`. Verifique también que su servidor permita `Access-Control-Allow-Credentials: true`.
-
Configuraciones conflictivas entre el servidor y el cliente.
Revise cuidadosamente la configuración CORS del lado del servidor junto con la configuración del lado del cliente. Los desajustes (p. ej., el servidor solo permite solicitudes GET pero el cliente envía POST) causarán errores de CORS.
CORS y Mejores Prácticas de Seguridad
Si bien CORS permite un acceso controlado de origen cruzado, es esencial seguir las mejores prácticas de seguridad para prevenir vulnerabilidades:
- Evite usar
*en la cabeceraAccess-Control-Allow-Originen producción. Esto permite que todos los orígenes accedan a sus recursos, lo que puede ser un riesgo de seguridad. En su lugar, especifique los orígenes exactos que están permitidos. - Considere cuidadosamente qué métodos y cabeceras permitir. Solo permita los métodos y cabeceras que sean estrictamente necesarios para que su aplicación funcione correctamente.
- Implemente mecanismos de autenticación y autorización adecuados. CORS no es un sustituto de la autenticación y la autorización. Asegúrese de que su API esté protegida por medidas de seguridad apropiadas.
- Valide y sanitice todas las entradas del usuario. Esto ayuda a prevenir ataques de Cross-Site Scripting (XSS) y otras vulnerabilidades.
- Mantenga actualizada la configuración CORS del lado del servidor. Revise y actualice regularmente su configuración de CORS para asegurarse de que se alinee con los requisitos de seguridad de su aplicación.
CORS en Diferentes Entornos de Desarrollo
Los problemas de CORS pueden manifestarse de manera diferente en varios entornos de desarrollo y tecnologías. A continuación, se muestra cómo abordar CORS en algunos escenarios comunes:
Entornos de Desarrollo Local
Durante el desarrollo local, los problemas de CORS pueden ser particularmente molestos. Los navegadores a menudo bloquean las solicitudes desde su servidor de desarrollo local (p. ej., localhost:3000) a una API remota. Varias técnicas pueden aliviar este problema:
- Extensiones de Navegador: Extensiones como "Allow CORS: Access-Control-Allow-Origin" pueden deshabilitar temporalmente las restricciones de CORS para fines de prueba. Sin embargo, *nunca* las use en producción.
- Servidores Proxy: Configure un servidor proxy que reenvíe las solicitudes desde su servidor de desarrollo local a la API remota. Esto efectivamente hace que las solicitudes sean de "mismo origen" desde la perspectiva del navegador. Herramientas como
http-proxy-middleware(para Node.js) son útiles para esto. - Configurar CORS en el Servidor: Incluso durante el desarrollo, es una buena práctica configurar su servidor de API para permitir explícitamente las solicitudes desde su origen de desarrollo local (p. ej.,
http://localhost:3000). Esto simula una configuración CORS del mundo real y le ayuda a detectar problemas temprano.
Entornos sin Servidor (Serverless) (p. ej., AWS Lambda, Google Cloud Functions)
Las funciones sin servidor a menudo requieren una configuración cuidadosa de CORS. Muchas plataformas sin servidor proporcionan soporte CORS integrado, pero es crucial configurarlo correctamente:
- Configuraciones Específicas de la Plataforma: Utilice las opciones de configuración CORS integradas de la plataforma. AWS Lambda, por ejemplo, le permite especificar orígenes, métodos y cabeceras permitidos directamente en la configuración de API Gateway.
- Middleware/Bibliotecas: Para una mayor flexibilidad, puede usar middleware o bibliotecas para manejar CORS dentro del código de su función sin servidor. Esto es similar a los enfoques utilizados en entornos de servidor tradicionales (p. ej., usando el paquete `cors` en funciones Lambda de Node.js).
- Considere el Método `OPTIONS`: Asegúrese de que su función sin servidor maneje las solicitudes
OPTIONScorrectamente. Esto a menudo implica crear una ruta separada que devuelva las cabeceras CORS apropiadas.
Desarrollo de Aplicaciones Móviles (p. ej., React Native, Flutter)
CORS es una preocupación menos directa para las aplicaciones móviles nativas (Android, iOS), ya que no suelen aplicar la política del mismo origen de la misma manera que los navegadores web. Sin embargo, CORS aún puede ser relevante si su aplicación móvil usa una vista web (web view) para mostrar contenido web o si está usando frameworks como React Native o Flutter que aprovechan JavaScript:
- Vistas Web: Si su aplicación móvil usa una vista web para mostrar contenido web, se aplican las mismas reglas de CORS que en un navegador web. Configure su servidor para permitir solicitudes desde el origen del contenido web.
- React Native/Flutter: Estos frameworks usan JavaScript para realizar solicitudes de API. Si bien el entorno nativo podría no aplicar CORS directamente, los clientes HTTP subyacentes (p. ej.,
fetch) aún pueden exhibir un comportamiento similar a CORS en ciertas situaciones. - Clientes HTTP Nativos: Al realizar solicitudes de API directamente desde código nativo (p. ej., usando OkHttp en Android o URLSession en iOS), CORS generalmente no es un factor. Sin embargo, aún debe considerar las mejores prácticas de seguridad como la autenticación y autorización adecuadas.
Consideraciones Globales para la Configuración de CORS
Al configurar CORS para una aplicación accesible globalmente, es crucial considerar factores como:
- Soberanía de los Datos: Las regulaciones en algunas regiones exigen que los datos residan dentro de la región. CORS puede estar involucrado al acceder a recursos a través de fronteras, lo que podría entrar en conflicto con las leyes de residencia de datos.
- Políticas de Seguridad Regionales: Diferentes países pueden tener diferentes regulaciones y directrices de ciberseguridad que influyen en cómo se debe implementar y asegurar CORS.
- Redes de Entrega de Contenido (CDNs): Asegúrese de que su CDN esté configurada correctamente para pasar las cabeceras CORS necesarias. Las CDNs mal configuradas pueden eliminar las cabeceras CORS, lo que lleva a errores inesperados.
- Balanceadores de Carga y Proxies: Verifique que cualquier balanceador de carga o proxy inverso en su infraestructura esté manejando correctamente las solicitudes 'preflight' y pasando las cabeceras CORS.
- Soporte Multilingüe: Considere cómo interactúa CORS con las estrategias de internacionalización (i18n) y localización (l10n) de su aplicación. Asegúrese de que las políticas de CORS sean consistentes en las diferentes versiones de idioma de su aplicación.
Pruebas y Depuración de CORS
Probar y depurar CORS de manera efectiva es vital. Aquí hay algunas técnicas:
- Herramientas de Desarrollador del Navegador: La consola de desarrollador del navegador es su primera parada. La pestaña "Red" mostrará las solicitudes 'preflight' y las respuestas, revelando si las cabeceras CORS están presentes y configuradas correctamente.
- Herramienta de Línea de Comandos `curl`: Use `curl -v -X OPTIONS
` para enviar manualmente solicitudes 'preflight' e inspeccionar las cabeceras de respuesta del servidor. - Verificadores de CORS en Línea: Numerosas herramientas en línea pueden ayudar a validar su configuración de CORS. Simplemente busque "CORS checker".
- Pruebas Unitarias y de Integración: Escriba pruebas automatizadas para verificar que su configuración de CORS funciona como se espera. Estas pruebas deben cubrir tanto las solicitudes de origen cruzado exitosas como los escenarios en los que CORS debería bloquear el acceso.
- Registro y Monitoreo: Implemente un registro para rastrear eventos relacionados con CORS, como solicitudes 'preflight' y solicitudes bloqueadas. Monitoree sus registros en busca de actividad sospechosa o errores de configuración.
Conclusión
El Intercambio de Recursos de Origen Cruzado (CORS) es un mecanismo de seguridad vital que permite el acceso controlado de origen cruzado a los recursos web. Comprender cómo funciona CORS, especialmente las solicitudes 'preflight', es crucial para construir aplicaciones web seguras y confiables. Siguiendo las mejores prácticas descritas en esta guía, puede manejar eficazmente los problemas de CORS y proteger su aplicación de posibles vulnerabilidades. Recuerde priorizar siempre la seguridad y considerar cuidadosamente las implicaciones de su configuración de CORS.
A medida que evoluciona el desarrollo web, CORS seguirá siendo un aspecto crítico de la seguridad web. Mantenerse informado sobre las últimas mejores prácticas y técnicas de CORS es esencial para construir aplicaciones web seguras y accesibles a nivel mundial.